/*
 * 
 * @autor libing
 * @2015/07/07
 * 组件基类
 * 依赖jQuery和artTemplate
 *
 * TODO: 组件嵌套，类名支持作用域
 */
 (function($){
    "use strict";
    window.Component = Class.extend({
        $: $,
        // 指定模板渲染引擎，默认为artTemplate：https://github.com/aui/artTemplate
        _tplEngine: template,
        // html标签上存实例的data名称
        _instanceReference: 'XY_component_instance',
        _wrapperClass: 'XY_component_wrapper',
        // 是否外部直接传入dom
        _isExternalDom: false,
        // 事件的订阅函数
        _onCallbacks: null,

        // 根节点
        $root: null,
        // 数据
        data: null,
        // 参数
        option: null,

        // 视图
        tplId: null,
        tplUrl: null,
        tpl: null,

        // 是否异步渲染，异步拉取模板或者数据时，要设为true
        asynRender: false,

        // dom事件，格式为：
        // {".button:click": "method"}
        // 处理函数接受两个参数：出发事件的jquery节点，event,函数内的this指向组件实例
        events: null,
        // 给dom节点指定名字，方便调用
        // 格式如：{name:'.input-name', submitBtn:'.btn-submit'}
        // 通过 this.$name, this.$submitBtn 调用,渲染后才生效
        hooks: null,

        // 初始化
        init: function(data, option){
          this.initDefaultObj();
          if(data instanceof this.$){
            // 直接传入dom节点
            this._isExternalDom = true;
            this.$root = data;
            this.$root.addClass(this._wrapperClass);
            this.$root.data(this._instanceReference, this);
            this._initHooks();
            this._initEventHandlers();
          }else if(typeof data === 'object'){
            this.data = this.$.extend({}, this.data, data);
          }
          if(typeof option === 'object'){
            this.option = this.$.extend({}, this.option, option);
          }
          this._createRoot();
          if(this.asynRender === false){
            this.render();
          }
          this.construct(data, option);
        },

        initDefaultObj: function(){
          // 这些对象不能直接定义在外面，否则在原型链上被其他组件共享
          this.data = this.data || {};
          this.option = this.option || {};
          this._onCallbacks = {};
        },

        // 构造函数，子类实现
        construct: function(data, option){},

        // 保存数据前验证
        validate: function(key, val){},

        setData: function(key, val, noRender){
          var that = this;
          if(this.validate.apply(this, arguments) === false){
            return;
          }
          if(typeof key === 'object'){
            this.data = this.$.extend({}, this.data, key);
          }else{
            this.data[key] = val;
          }
          window.setTimeout(function(){
            that.trigger('update', key, val);
          });
          if(noRender !== true){
            this.render();
          }
          return this;
        },

        getData: function(){
          return this.data();
        },

        render: function(){
          var that = this;
          if(this._isExternalDom) return this.$root;

          if(this._tplCompiledFn){
            this._renderHtml();
          }else{
            if(typeof this.tpl === 'function'){
              var source = this.tpl();
              this._tplCompiledFn = this._tplEngine.compile(source);
              this._renderHtml();
            }else if(this.tplId){
              var source = $('#'+this.tplId).html();
              this._tplCompiledFn = this._tplEngine.compile(source);
              this._renderHtml();
            }else if(this.tplUrl){
              this.$.get(this.tplUrl, function(html){
                that._tplCompiledFn = that._tplEngine.compile(html);
                that._renderHtml();
              });
            }else{
              return false;
            }
          }
        },

        _renderHtml: function(){
          var that = this;
          var html = this._tplCompiledFn(this.data);
          this.$root.html(html);
          this._initHooks();
          this.afterRender();
          // 异步触发，保证在后面绑定，初始化时也能执行到
          window.setTimeout(function(){
            that.trigger('render', that.$root, that.data, that.option);
          });
        },

        afterRender: function(){},

        _createRoot: function(){
          if(!this.$root){
            this.$root = this.$('<div class="'+ this._wrapperClass +'"></div>');
            this.$root.data(this._instanceReference, this);
            this._initEventHandlers();
          }
        },

        getRoot: function(){
          if(!this.$root){
            console.warn('root init fail');
            return false;
          }
          return this.$root;
        },

        _initEventHandlers: function(){
          if(!this.events) return;
          var that = this;
          for(var itemkey in this.events){
            (function(key){
              var keys = key.split(':'),
                  selector = keys[0],
                  eventName = keys[1];
              // 优先成从hook里取
              if(that.hooks && that.hooks[selector]){
                selector = that.hooks[selector];
              }
              if(selector && eventName && that[that.events[key]]){
                  that.$root.delegate(selector, eventName, function(e){
                    that[that.events[key]].call(that, e, $(this));
                  });
              }
            })(itemkey);
          }
        },

        _initHooks: function(){
          if(!this.hooks || !this.$root) return;
          var that = this;
          for(var name in this.hooks){
            this['$'+name] = this.$root.find(this.hooks[name]);
          }
        },

        on: function(name, callback, context){
          if(!this._onCallbacks[name]){
            this._onCallbacks[name] = [];
          }
          this._onCallbacks[name].push({
            callback: callback,
            context: context ? context : window
          });
        },

        trigger: function(type){
          var callbacks = this._onCallbacks[type], arg;
          if(!callbacks || callbacks.length === 0) return;
          for(var i=0,len=callbacks.length; i<len; i++){
            arg = [].slice.call(arguments, 1);
            callbacks[i].callback.apply(callbacks[i].context, arg);
          }
        },

        subscribe: function(name, fn, context){
          Component.Message.subscribe(name, fn, context);
        },

        publish: function(name){
          //var params = [].slice.call(arguments, 0);
          Component.Message.publish.apply(Component.Message, arguments);
        }

    });
  
    /*
     * 消息中心
     */
    Component.Message = {
      callbacks: {},

      subscribe: function(name, fn, context){
        if(!this.callbacks[name]){
          this.callbacks[name] = [];
        }
        this.callbacks[name].push({
          fn: fn,
          context: context || window
        });
      },

      publish: function(name){
        var params = [].slice.call(arguments, 1),
            list = this.callbacks[name];
        if(list && list.length > 0){
          $.each(list, function(i, item){
            item.fn.apply(item.context, params);
          });
        }
      }

    };
    
    // 存储dom扫描生成的实例
    Component.instances = {};

    // 扫描页面dom生成组件
    Component.initTagComponent = function(){
        $('[data-component]').each(function(){
          var $this = $(this),
              componentName, componentClass, dataStr, optionStr, data, option, instance, name;
          componentName = $this.data('component');
          name = $this.data('name');
          if(componentName.indexOf('.') === -1){
            componentClass = window[componentName];
          }else{
            var names = componentName.split('.'),
                componentClass = window;
            for(var i=0,l=names.length; i<l; i++){
              componentClass = componentClass[names[i]] || null;
              if(!componentClass) break;
            }
          }

          if(componentClass){
            dataStr = $this.data('data');
            optionStr = $this.data('option');
            data = (typeof dataStr == 'object') ? dataStr : (dataStr ? $.parseJSON(dataStr) : null);
            option = (typeof optionStr == 'object') ? optionStr : (optionStr ? $.parseJSON(optionStr) : null);
            if($this.children().length > 0){
              // dom节点提供内容，只通过类绑定行为
              instance = new componentClass($this, option);
            }else{
              instance = new componentClass(data, option);
              $this.html(instance.getRoot());
            }
            if(name){
              Component.instances[name] = instance;
            }
          }
        });
      };

    // 根据selector或者jquery dom获取组件
    Component.getComponent = function(selector){
        var that = this,
            instanceReference = 'XY_component_instance';
        if(selector instanceof $){
          var instance = selector.data(instanceReference);
          if(!instance){
            instance = selector.children().eq(0).data(instanceReference);
          }
          return instance || null;
        }else{
          var $node = $(selector);
          if($node.length > 1){
            var instanceList = [];
            $node.each(function(){
              var instance = that.getComponent($(this));
              if(instance){
                instanceList.push(instance);
              }
            });
            return instanceList;
          }else{
            return this.getComponent($node);
          }
        }
      };

    Component.insertComponent = function(selector, instance){
      var $container = selector instanceof $ ? selector : $(selector);
      $container.html(instance.getRoot());
      return instance;
    };

    $.fn.insertComponent = function(instance){
      return Component.insertComponent($(this), instance);
    }

    $(function(){
      Component && Component.initTagComponent();
    });

 })(jQuery);

